#include "netlib/net/poller/epoll_poller.h"

#include <sys/epoll.h>
#include <sys/poll.h>

#include "netlib/base/logger.h"
#include "netlib/net/event/channel.h"
#include "netlib/net/event/event_loop.h"
#include "netlib/net/socket/socket_ops.h"

using namespace netlib;

static_assert(EPOLLIN == POLLIN, "epoll uses same flag values as poll");
static_assert(EPOLLPRI == POLLPRI, "epoll uses same flag values as poll");
static_assert(EPOLLOUT == POLLOUT, "epoll uses same flag values as poll");
static_assert(EPOLLRDHUP == POLLRDHUP, "epoll uses same flag values as poll");
static_assert(EPOLLERR == POLLERR, "epoll uses same flag values as poll");
static_assert(EPOLLHUP == POLLHUP, "epoll uses same flag values as poll");

const int netlib::net::EPollPoller::kInitEventListSize;

netlib::net::EPollPoller::EPollPoller(EventLoop* loop) :
    Poller(loop), epollfd_(epoll_create1(EPOLL_CLOEXEC)), events_(kInitEventListSize) {
	assert(epollfd_ != -1);
}

netlib::net::EPollPoller::~EPollPoller() { netlib::net::sockets::Close(epollfd_); }

Timestamp netlib::net::EPollPoller::Poll(int timeout, ChannelVector* activate_channels) {
	int n_events = epoll_wait(epollfd_, events_.data(), static_cast<int>(events_.size()), timeout);
	Timestamp now(Timestamp::Now());
	if (n_events > 0) {
		LOG_TRACE << n_events << " events happened";
		FillActivateChannels(n_events, activate_channels);
	} else if (n_events < 0) {
		LOG_SYSERR << "Poller::poll()";
	} else {
		LOG_TRACE << "nothing happended";
	}
	return now;
}

void netlib::net::EPollPoller::FillActivateChannels(int n_events,
                                                    ChannelVector* activate_channels) const {
	auto e_it = events_.begin();
	while (n_events--) {
		auto c_it = channels_.find(e_it->data.fd);
		assert(c_it != channels_.end());
		assert(c_it->second->Fd() == e_it->data.fd);
		c_it->second->SetRevents(e_it->events);
		activate_channels->push_back(c_it->second);
		++e_it;
	}
}

void netlib::net::EPollPoller::AddChannel(Channel* channel) {
	assert(channels_.find(channel->Fd()) == channels_.end());
	struct epoll_event ev {
		static_cast<unsigned>(channel->Events()), {}
	};
	ev.data.fd = channel->Fd();
	if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, channel->Fd(), &ev) < 0) {
		LOG_SYSERR << "EPollPoller::addChannel()";
	}
	channels_.emplace(channel->Fd(), channel);
	channel->SetIndex(0);
}

void netlib::net::EPollPoller::ResetChannel(Channel* channel) {
	assert(channels_.find(channel->Fd()) != channels_.end());
	assert(channels_.at(channel->Fd()) == channel);

	if (channel->IsNoneEvent()) {
		epoll_ctl(epollfd_, EPOLL_CTL_DEL, channel->Fd(), nullptr);
		// channels_.erase(channel->Fd());
		// channel->setIndex(-1);
	} else {
		struct epoll_event ev {
			static_cast<unsigned>(channel->Events()), {}
		};
		ev.data.fd = channel->Fd();
		epoll_ctl(epollfd_, EPOLL_CTL_MOD, channel->Fd(), &ev);
	}
}

void netlib::net::EPollPoller::RemovePollfd(Channel* channel) {
	epoll_ctl(epollfd_, EPOLL_CTL_DEL, channel->Fd(), nullptr);
	// channels_.erase(channel->fd());
}
